#pragma once

#include "comm/my_defs.h"
#include "comm/my_log.h"
#include "comm/my_queue.h"
#include "event/my_event.h"
#include "event/my_event_loop.h"

typedef enum {
    MY_AIO_READ         = 1,
    MY_AIO_WRITE        = 2,
    MY_AIO_READ_PING    = 3,
    MY_AIO_WRITE_PING   = 4
} my_aio_opcode_t;


typedef struct {
    my_aio_opcode_t         op;

    int                     fd;
    void                    *buf;
    size_t                  nbytes;
    off_t                   offset;

    // context
    my_dispatch_event_t     dispatch_event;

    int64_t                 setup_time;
    int64_t                 done_time;

    my_syscall_result_t     res;
} my_aio_request_t;


static inline void
my_aio_reqeuest_init(
    my_aio_request_t *req,
    my_aio_opcode_t op,
    int fd, void *buf, size_t nbytes, off_t offset,
    my_dispatch_handler_t handler, void *data)
{
    bzero(req, sizeof(my_aio_request_t));

    req->op = op;
    req->fd = fd;
    req->buf = buf;
    req->nbytes = nbytes;
    req->offset = offset;

    req->dispatch_event.handler = handler;
    req->dispatch_event.data = data;
    req->dispatch_event.save_data = req;
}


typedef struct {
    my_mutex_t              mutex;
    my_cond_t               cond;
    my_queue_t              extern_queue;
    my_atomic_int64_t       tasks;
    my_bool_t               stop;
} my_aio_loop_data_t;


static inline void
my_aio_loop_data_init(my_aio_loop_data_t *loop_data)
{
    bzero(loop_data, sizeof(my_aio_loop_data_t));
    my_mutex_init(&loop_data->mutex);
    my_cond_init(&loop_data->cond);
    my_queue_init(&loop_data->extern_queue);
}


typedef struct {
    int                     n_read_threads;
    int                     n_write_threads;

    my_aio_loop_data_t      read_loop_data;
    my_aio_loop_data_t      write_loop_data;

    my_thread_base_t        *read_threads;
    my_thread_base_t        *write_threads;
} my_aio_manager_t;


void my_aio_manager_start(my_aio_manager_t *aio_manager,
    int n_read_threads, int n_write_threads);

void my_aio_manager_stop(my_aio_manager_t *aio_manager);

// the following interfaces can only be called in the worker thread.
void my_aio_post_event(my_aio_manager_t *aio_manager,
    my_aio_request_t *req);


extern char *my_aio_thread_tag;

static inline my_bool_t
current_thread_is_aio_thread()
{
    my_thread_base_t *thread_base = current_thread_base();
    if (thread_base == NULL) {
        return MY_FALSE;
    }

    if (thread_base->tag != my_aio_thread_tag) {
        return MY_FALSE;
    }

    return MY_TRUE;
}
